package model.events;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import model.enumTypes.CSGrenadeType;
import model.path.CSPlayerPath;
import constants.CSConstants;
import constants.GUIConstants;






/**
 * This class describes a grenade event : the grenade's path,
 * its type, the throw time and the throw point.
 * 
 * @author Urbanek Lucas (aka NaG)
 */
public class CSGrenadeEvent implements Serializable {
	
	private Point player;
	private List<Point> path;
	private double startTime;
	private CSGrenadeType type;
	
	// Very private members =p :
	private double totalLength;
	private int segmentsCount;
	
	private CSPlayerPath parent;
	
	/**
	 * The builder of the CSGrenadeEvent class.
	 * It is quite simple to understand.
	 * 
	 * @param player_
	 * @param first
	 * @param start
	 * @param type_
	 */
	public CSGrenadeEvent(Point player_, Point first, double start, CSGrenadeType type_, CSPlayerPath parent_) {
		player = player_;
		path = new ArrayList<Point>();
		path.add(player);
		path.add(first);
		startTime = start;
		type = type_;
		
		totalLength = player.distance(first);
		segmentsCount = 1;
		parent = parent_;
	}

	public List<Line2D> getLine2DArrayList() {
		if (path==null || path.size()<2)
			return new ArrayList<Line2D>();
		
		List<Line2D> ret = new ArrayList<Line2D>();
		for (int i=0; i<path.size()-1; i++)
			ret.add(new Line2D.Float(path.get(i).x,
					path.get(i).y,
					path.get(i+1).x,
					path.get(i+1).y));
		return ret;
	}
	
	/**
	 * This method appends a point to the grenade's path.
	 * 
	 * @param pt
	 */
	public void appendPoint(Point pt) {
		totalLength += path.get(segmentsCount-1).distance(pt);
		segmentsCount++;
		path.add(pt);
		
	}
	
	/**
	 * This method deletes the grenade's path node
	 * at the given index.
	 * 
	 * @param index
	 */
	public void deletePoint(int index) {
		path.remove(index);
		segmentsCount--;
		if (segmentsCount == 0)
			parent.removeGrenadeEvent(this.getStartTime());
	}
	
	/**
	 * This method moves the grenade's path node
	 * at the given index to the given new position.
	 * 
	 * @param index
	 * @param x_
	 * @param y_
	 */
	public void movePoint(int index, int x_, int y_) {
		path.get(index).x = x_;
		path.get(index).y = y_;
	}
	
	/**
	 * This method moves the grenade's path node
	 * at the given index to the given new position.
	 * 
	 * @param index
	 * @param pt
	 */
	public void movePoint(int index, Point pt) {
		this.movePoint(index, pt.x, pt.y);
	}
	
	/**
	 * Returns the index of the possibly selected index
	 * when the user clicks at the given point.
	 * 
	 * Returns -1 if there is no index in the MIN_SELECTION_DISTANCE
	 * range.
	 * 
	 * @param pt
	 * @return int
	 */
	public int getPossibleSelectedIndex(Point pt) {
		for (int i=1;i<path.size();i++) {
			if (path.get(i).distance(pt)<GUIConstants.MIN_SELECTION_DISTANCE)
				return i;
		}
		return -1;
	}
	
	/**
	 * Returns the position of the grenade at the
	 * given time.
	 * 
	 * If the given time is out of the event's range,
	 * returns null.
	 * 
	 * @param time
	 * @return
	 */
	public Point getPositionAtTime(double time) {
		if (time<startTime || time>startTime+CSConstants.GRENADE_TIMEOUT)
			return null;
		double relativeTime = time-startTime;
		double progress = relativeTime/CSConstants.GRENADE_TIMEOUT;
		int segment = (int)Math.floor(progress*segmentsCount);
		
		if (segment == path.size()-1)
			return path.get(segment);
		
		// We have "0<=Segment<path.size()-2"
		Point p1 = path.get(segment);
		Point p2 = path.get(segment+1);
		
		double innerProgress = progress*segmentsCount-segment;
		
		return new Point(p1.x+(int)Math.floor(innerProgress*(p2.x-p1.x)),p1.y+(int)Math.floor(innerProgress*(p2.y-p1.y)));
	}
	
	public int size() {
		return path.size();
	}
	
	public Point getNode(int i) {
		return path.get(i);
	}
	
	public double getStartTime() {
		return startTime;
	}

	public void setStartTime(double startTime) {
		this.startTime = startTime;
		setStartPosition(parent.getPositionAt(startTime));
	}

	public CSGrenadeType getType() {
		return type;
	}

	public void setType(CSGrenadeType type) {
		this.type = type;
	}

	public Point getStartPosition() {
		return player;
	}

	public void setStartPosition(Point player) {
		this.player = player;
		path.set(0,player);
	}
}
